From 463054383fbeef889b409a7f843df5365288e2a0 Mon Sep 17 00:00:00 2001
From: Christian Kastner <ckk@kvr.at>
Date: Tue, 13 Jun 2023 14:21:52 +0200
Subject: [PATCH] Add option to read username/password from file (#781)

* Add option to read username/password from file

Upstream: https://github.com/alanxz/rabbitmq-c/commit/463054383fbeef889b409a7f843df5365288e2a0
Signed-off-by: Fabrice Fontaine <fontaine.fabrce@gmail.com>
---
 tools/common.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/tools/common.c b/tools/common.c
index 73b47e25..7efe557b 100644
--- a/tools/common.c
+++ b/tools/common.c
@@ -18,6 +18,11 @@
 #include "compat.h"
 #endif
 
+/* For when reading auth data from a file */
+#define MAXAUTHTOKENLEN 128
+#define USERNAMEPREFIX "username:"
+#define PASSWORDPREFIX "password:"
+
 void die(const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
@@ -125,6 +130,7 @@ static char *amqp_vhost;
 static char *amqp_username;
 static char *amqp_password;
 static int amqp_heartbeat = 0;
+static char *amqp_authfile;
 #ifdef WITH_SSL
 static int amqp_ssl = 0;
 static char *amqp_cacert = "/etc/ssl/certs/cacert.pem";
@@ -147,6 +153,8 @@ struct poptOption connect_options[] = {
      "the password to login with", "password"},
     {"heartbeat", 0, POPT_ARG_INT, &amqp_heartbeat, 0,
      "heartbeat interval, set to 0 to disable", "heartbeat"},
+    {"authfile", 0, POPT_ARG_STRING, &amqp_authfile, 0,
+     "path to file containing username/password for authentication", "file"},
 #ifdef WITH_SSL
     {"ssl", 0, POPT_ARG_NONE, &amqp_ssl, 0, "connect over SSL/TLS", NULL},
     {"cacert", 0, POPT_ARG_STRING, &amqp_cacert, 0,
@@ -158,6 +166,50 @@ struct poptOption connect_options[] = {
 #endif /* WITH_SSL */
     {NULL, '\0', 0, NULL, 0, NULL, NULL}};
 
+void read_authfile(const char *path) {
+  size_t n;
+  FILE *fp = NULL;
+  char token[MAXAUTHTOKENLEN];
+
+  if ((amqp_username = malloc(MAXAUTHTOKENLEN)) == NULL ||
+      (amqp_password = malloc(MAXAUTHTOKENLEN)) == NULL) {
+    die("Out of memory");
+  } else if ((fp = fopen(path, "r")) == NULL) {
+    die("Could not read auth data file %s", path);
+  }
+
+  if (fgets(token, MAXAUTHTOKENLEN, fp) == NULL ||
+      strncmp(token, USERNAMEPREFIX, strlen(USERNAMEPREFIX))) {
+    die("Malformed auth file (missing username)");
+  }
+  strncpy(amqp_username, &token[strlen(USERNAMEPREFIX)], MAXAUTHTOKENLEN);
+  /* Missing newline means token was cut off */
+  n = strlen(amqp_username);
+  if (amqp_username[n - 1] != '\n') {
+    die("Username too long");
+  } else {
+    amqp_username[n - 1] = '\0';
+  }
+
+  if (fgets(token, MAXAUTHTOKENLEN, fp) == NULL ||
+      strncmp(token, PASSWORDPREFIX, strlen(PASSWORDPREFIX))) {
+    die("Malformed auth file (missing password)");
+  }
+  strncpy(amqp_password, &token[strlen(PASSWORDPREFIX)], MAXAUTHTOKENLEN);
+  /* Missing newline means token was cut off */
+  n = strlen(amqp_password);
+  if (amqp_password[n - 1] != '\n') {
+    die("Password too long");
+  } else {
+    amqp_password[n - 1] = '\0';
+  }
+
+  (void)fgetc(fp);
+  if (!feof(fp)) {
+    die("Malformed auth file (trailing data)");
+  }
+}
+
 static void init_connection_info(struct amqp_connection_info *ci) {
   ci->user = NULL;
   ci->password = NULL;
@@ -237,6 +289,8 @@ static void init_connection_info(struct amqp_connection_info *ci) {
   if (amqp_username) {
     if (amqp_url) {
       die("--username and --url options cannot be used at the same time");
+    } else if (amqp_authfile) {
+      die("--username and --authfile options cannot be used at the same time");
     }
 
     ci->user = amqp_username;
@@ -245,11 +299,23 @@ static void init_connection_info(struct amqp_connection_info *ci) {
   if (amqp_password) {
     if (amqp_url) {
       die("--password and --url options cannot be used at the same time");
+    } else if (amqp_authfile) {
+      die("--password and --authfile options cannot be used at the same time");
     }
 
     ci->password = amqp_password;
   }
 
+  if (amqp_authfile) {
+    if (amqp_url) {
+      die("--authfile and --url options cannot be used at the same time");
+    }
+
+    read_authfile(amqp_authfile);
+    ci->user = amqp_username;
+    ci->password = amqp_password;
+  }
+
   if (amqp_vhost) {
     if (amqp_url) {
       die("--vhost and --url options cannot be used at the same time");
